Ana içeriğe geç
  1. 100 Günde SwiftUI Notları/

29.Gün - SwiftUI List, App Bundle ve String İşlemleri

Bu bölümde SwiftUI view türlerinden olan List’i göreceğiz, App Bundle nedir sorusuna cevap arayacağız ve string işlemlerinin nasıl yapıldığını inceleyeceğiz.

SwiftUI List Nedir? Nasıl Kullanılır? #

SwiftUI’nin tüm view türleri arasında List en güveneceğiniz türdür. UIKit’de List ’in karşılığı UITableView idi ve o da aynı şekilde kullanılıyordu.

List ’in görevi, kayan bir veri tablosu sağlamaktır. Aslında, kullanıcı girdisi istemek yerine, veri sunumu için kullanılması dışında Form ile hemen hemen aynıdır.

Tıpkı Form ’da olduğu gibi, List ’e de satırlarda işlenmelerini sağlamak için bir dizi statik view sağlayabiliriz.

List {
    Text("Hello World")
    Text("Hello World")
    Text("Hello World")
}

Swiftui list view

Bir array ve aralıktan dinamik olarak satır oluşturmak için ForEach ’de kullanabiliriz.

List {
    ForEach(0..<5) {
        Text("Dynamic row \($0)")
    }
}

Daha da ilginç olarak, statik ve dinamik satırları karışık bir şekilde kulanabiliriz.

List {
    Text("Static row 1")
    Text("Static row 2")

    ForEach(0..<5) {
        Text("Dynamic row \($0)")
    }

    Text("Static row 3")
    Text("Static row 4")
}

Ve tabiki listemizin okunmasını kolaylaştırmak için bunu bölümlerle birleştirebiliriz.

List {
    Section("Section 1") {
        Text("Static row 1")
        Text("Static row 2")
    }

    Section("Section 2") {
        ForEach(0..<5) {
            Text("Dynamic row \($0)")
        }
    }

    Section("Section 3") {
        Text("Static row 3")
        Text("Static row 4")
    }
}

Swiftui list with section

Bu listenin daha önce sahip olduğumuz forma benzediğini fark edeceksiniz, ancak istStyle() modifier’ını kullanarak listenin nasıl görüneceğini aşağıdaki gibi ayarlayabiliriz.

.listStyle(.grouped)

SwiftUI List Style Grouped

Şu ana kadar gördüğümüz her şey Form ’un yanı sıra List ile de sorunsuz çalışıyor, dinamik içerik bile. Ancak List ’in yapabildiği ve Form ’un yapamadığı şey, ForEach ’e ihtiyaç duymadan satırlarını tamamen dinamik içerikten oluşturmaktır.

Dolayısıyla, listemizin tamamı dinamik satırlardan oluşuyorsa bunu basitçe yazabiliriz;

List(0..<5) {
    Text("Dynamic row \($0)")
}

Bu projede List ’i biraz farklı kullanacağız çünkü bir string array üzerinde döngü oluşturacağız. ForEach ’i sabit kodlanmış (0..<5) ya da değişken verilere dayanan (0..<students.count) aralıklarla kullandık.

String ve sayılardan oluşan, bu değerleri benzersiz kılan tek şey değerlerin kendisidir. Yani [2, 4, 6, 8, 10] array’ine sahipsek, o zaman bu sayıların kendileri benzersiz tanımlayacılardır.

Bu tür liste verileriyle çalışırken id: \.self bu şekilde kullanılır;

struct ContentView: View {
    let people = ["Finn", "Leia", "Luke", "Rey"]

    var body: some View {
        List(people, id: \.self) {
            Text($0)
        }
    }
}

Bu ForEach ile aynı şekilde çalışır, bu nedenle statik ve dinamik satırları karıştırmak isteseydik, yukarıdaki kodun yerine şunu yazabilirdik;

List {
    Text("Static Row")

    ForEach(people, id: \.self) {
        Text($0)
    }

    Text("Static Row")
}

SwiftUI App Bundle Nedir? #

Image view kullandığımızda, SwiftUI resmi bulmak için uygulamamızın asset kataloğuna bakmayı bilir ve hatta resmi otomatik olarak ayarlar, böylece mevcut ekran çözünürlüğü için doğru resmi yükler, @2x veya @3x vs.

Metin dosyaları, XML veya JSON gibi diğer veriler için daha fazla iş yapmamız gerekir.

Xcode, iOS uygulamamızı oluştururken “bundle” adı verilen bir şey oluşturur. Bu, macOS dahil olmak üzere Apple’ın tüm platformlarında gerçekleşir ve sistemin tek bir uygulama için tüm dosyaları tek bir yerde saklamasına olanak tanır.

Gelecekte tek bir iOS app bundle içinde Siri uzantıları, iMessage uygulamaları, widget’lar ve daha fazlası gibi şeyler yazmamıza olanak tanıyan birden fazla paketi tek bir uygulamaya nasıl dahil edebileceğinizi öğreneceğiz.

Pakedin içine yerleştirdiğimiz bir dosyayı aramak istediğimizde URL adında yeni bir veri türünü kullanırız. Bu standart manada bildiğimiz https://gorkem.co gibi URL’lerdir. Fakat URL’ler sadece web adreslerini saklamaktan daha güçlüdür ve dosyalarımızın konumlarını da saklayabilirler.

Ana uygulama paketimizdeki bir dosyanın URL’sini okumak istiyorsak Bundle.main.url() methodunu kullanırız. Dosya varsa bize geri gönderilir, aksi takdirde nil geri alırız, yani bu optional bir URL’dir. Bu yüzden şu şekilde unwrap etmemiz gerekir;

if let fileURL = Bundle.main.url(forResource: "some-file", withExtension: "txt") {
    // we found the file in our bundle!
    // bundle içinde dosya bulundu!
}

URL ’nin içinde ne olduğu gerçekten önemli değildir, çünkü iOS tahmin edilmesi imkansız yollar kullanır. Uygulamamız kendi sanal alanında yaşar ve bunun dışını okumaya çalışmamalıyız.

Bir URL’ye sahip olduğumuzda, bunu özel bir initializer ile bir string’e yükleyebiliriz: String(contentsOf:) Buna bir dosya URL’si veririz ve eğer yüklenebiliyorsa, o dosyanın içeriğini içeren bir string’i geri gönderir. Yüklenemezse hata veriri bu nedenle try veya try? kullanarak çağırmamız gerekir;

if let fileContents = try? String(contentsOf: fileURL) {
    // we loaded the file into a string!
    // dosya içeriğini string'e yükledik
}

Dosyanın içeriğine sahip olduğumuzda, onunla ne istersek yapabiliriz çünkü bu sadece normal bir string’tir.

SwiftUI String ile Çalışmak #

iOS bize stringler ile çalışmak için, onları bir diziye bölme, boşlukları kaldırma ve hatta yazım denetimi yapma gibi gerçekten güçlü API’ler sunuyor. Bunlardan bazılarına daha önce bakmıştık, ancak daha fazlası var.

Bu uygulamada (Word Scramble) , oyunu başlatmak için app bundle’dan 10.000’den fazla sekiz harfli kelime içeren bir dosya yükleyeceğiz. Bu sözcükler her satırda bir tane olacak şekilde saklanır, bu nedenle asıl istediğimiz bu string’i rastgele bir tane seçebilmek için string array’e bölmektir.

Swift String Bölme #

Swift bize components(separatedBy:) adında, tek bir string’i başka bir string’in bulunduğu yerden parçalayarak bir string array’e dönüştürebilen bir method sunar. Örneğin aşağıdaki kod [”a”, ”b”, “c”] array’ini oluşturacaktır.

let input = "a b c"
let letters = input.components(separatedBy: " ")

Kelimelerin satır sonlarıyla ayrıldığı bir string’imiz var, bu yüzden bunu bir string array’e dönüştürmek için bunu bölmemiz gerekir.

Programlamada satır sonlarını temsil etmek için özel bir karakter kullanırız : \n Yani şöyle bir kod yazabiliriz;

let input = """
            a
            b
            c
            """
let letters = input.components(separatedBy: "\n")

Hangi string’i bölersek bölelim, sonuç bir string array’i olacaktır. Buradan, letters[0] veya letters[2] gibi bir array indexleme yaparak tek tek değerleri okuyabiliriz, ancak Swift bize kullanışlı bir seçenek daha sunar: randomElement() methodu array’den rastgele bir öğe döndürür.

Örneğin, aşağıdaki kod array’den rastgele bir harf okuyacaktır;

let letter = letters.randomElement()

Swift String Trim #

Bir başka kullanışlı string yöntemi de trimmingCharacters(in:) olup Swift’ten bir array’in başından ve sonundan belirli karakterleri kaldırmasını ister. Bu CharacterSet adında yeni bir tür kullanır, ancak çoğu zaman belirli bir davranış isteriz: boşlukları ve yeni satırları kaldırmak.

Bu davranış o kadar yaygındır ki doğrudan CharacterSet yapısına yerleştirilmiştir, bu nedenle Swift’ten bir array’in başındaki ve sonundaki tüm boşlukları bu şekilde kırpmasını isteyebiliriz;

let trimmed = letter?.trimmingCharacters(in: .whitespacesAndNewlines)

Swift UITextChecker #

İnceleyeceğimiz son bir string işlevi var ve bu da yanlış yazılmış kelimeleri kontrol etme yeteneği.

Bu yetenek UITextChecker sınıfı ile sağlanır. Bunun farkında olmayabilirsiniz, ancak bu adın “UI” kısmı beraberinde iki ek anlam taşır;

  1. Bu sınıf UIKit’den geliyor. Ancak bu, tüm eski kullanıcı arayüzü framewrok’ünü yüklediğimiz anlamına gelmiyor, aslında SwiftUI aracılığıyla otomatik olarak alıyoruz.
  2. Apple’ın eski dili Objective-C kullanılarak yazılmıştır. Kullanmak için Objective-C yazmamıza gerek yok, ancak Swift kullanıcıları için biraz hantal bir API var.

Bir string’de yanlış yazılmış sözcük olup olmadığını kontrol etmek toplam dört adımdan oluşur. İlk olarak, kontrol edilecek bir kelime ve bu string’i kontrol etmek için kullanabileceğimiz bir UITextChecker instance oluşturuyoruz;

let word = "swift"
let checker = UITextChecker()

İkinci olarak, checker’e array’in ne kadarını kontrol etmek istediğimizi söylememiz gerekir. Bir kelime işlem uygulamasında bir yazım denetimi olduğunu düşündüğümüzde, tüm belge yerine yalnızca kullanıcının seçtiği metni denetlemek isteriz.

Ancak, bir sorun var. Swift, array’ler ile çalışmak için çok akıllıca, çok gelişmiş bir yol kullanır. Bu da emoji gibi karmaşık karakterleri İngiliz alfabesini kullandığı şekilde kullanmasına olanak tanır. Ancak Objective-C harfleri saklamak için bu yöntemi kullanmaz, bu da Swift’ten tüm karakterlerimizin tüm uzunluğunu kullanarak bir Objective-C string aralığı oluşturmasını istememiz gerektiği anlamına gelir;

let range = NSRange(location: 0, length: word.utf16.count)

UTF-16 karakter kodlaması olarak adlandırılır. Objective-C’nin Swift’in string’lerini nasıl depoladığını anlayabilmesi için burada kullanıyoruz; ikisini birbirine bağlamak için güzel bir köprü formatı.

Üçüncü olaraki checker’dan kelimemizdeki yazım hatalarını nerede bulduğunu bildirmesini isteyebilir, kontrol edilecek aralığı, aralık içinde başlanacak bir konumu (”Sonrakini Bul” gibi şeyler yapabilmemiz için), sona ulaştığında başa dönülüp dönülmeyeceği ve sözlük için hangi dili kullanacağını iletebiliriz;

let misspelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en")

Bu, bize yanlış yazımın nerede bulunduğunu söyleyen başka bir Objective-C string aralığı gönderir. Objective-C’de optional kavramı yoktu, bunun yerine eksik verileri temsil etmek için özel değerlere güveniyordu.

Bu durumda, Objective-C aralığı boş olarak geri dönerse ( yani string doğru yazıldığı için yazım hatası yoksa) o zaman NSNotFound özel değerini geri alırız.

Böylece, bir hata olup olmadığını görmek için yazım sonucumuzu bu şekilde kontrol edebiliriz;

let allGood = misspelledRange.location == NSNotFound

Bu yazıyı İngilizce olarak da okuyabilirsiniz.
You can also read this article in English.

Bu yazı, SwiftUI Day 29 adresinde bulunan yazılardan kendim için aldığım notları içermektedir. Orjinal dersi takip etmek için lütfen bağlantıya tıklayın.